package hooks

import (
	"context"
	"fmt"
	"os"
	"strings"

	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"

	log "atomgit.com/atomgit_dev/alog/alogcore"
	"atomgit.com/atomgit_dev/git-hooks/pkg/config"
	"atomgit.com/atomgit_dev/git-hooks/pkg/internal/agitext"
	"atomgit.com/atomgit_dev/git-hooks/pkg/stream"
	"atomgit.com/atomgit_dev/git-hooks/pkg/streamio"
	pb "atomgit.com/atomgit_dev/satellite-proto/go"
)

// StartPreReceive calls satellite.PreReceiveHook to execute pre-receive hook.
func StartPreReceive(ctx context.Context) (int, error) {
	repository, err := agitext.GetPbRepositoryFromEnv()
	if err != nil {
		return 1, fmt.Errorf("fail to extract pb.Repository from env: %w", err)
	}
	keyID := os.Getenv("GL_ID")
	gitPushOptions := getPushOptionsFromEnv()
	log.Info(ctx, "begin to execute pre-receive hook",
		log.NewStringField("pushopts", strings.Join(gitPushOptions, ",")),
		log.NewStringField("user", keyID),
		log.NewField("RepositoryID", repository.GlRepositoryId),
	)

	conn, err := getSatelliteConn()
	if err != nil {
		return 1, fmt.Errorf("fail to create grpc client for pre-receive: %w", err)
	}
	defer conn.Close()

	ctx, cancel := context.WithTimeout(ctx, config.Cfg.PreReceiveTimeout)
	defer cancel()
	preReceiveHookStream, err := pb.NewHookServiceClient(conn).PreReceiveHook(ctx)
	if err != nil {
		return 1, fmt.Errorf("fail to get pre-receive hook stream: %w", err)
	}

	if err := preReceiveHookStream.Send(&pb.PreReceiveHookRequest{
		Repository:           repository,
		KeyId:                keyID,
		EnvironmentVariables: os.Environ(),
		GitPushOptions:       gitPushOptions,
	}); err != nil {
		return 1, fmt.Errorf("fail to send pre-receive hook request: %w", err)
	}

	f := sendFunc(streamio.NewWriter(func(p []byte) error {
		return preReceiveHookStream.Send(&pb.PreReceiveHookRequest{Stdin: p})
	}), preReceiveHookStream, os.Stdin)

	returnCode, err := stream.Handler(func() (stream.StdoutStderrResponse, error) {
		return preReceiveHookStream.Recv()
	}, f, os.Stdout, os.Stderr)
	if err != nil {
		log.WithError(err).Error(ctx, "error receiving data for pre-receive hook")
		if s, ok := status.FromError(err); ok {
			if s.Code() == codes.DeadlineExceeded {
				fmt.Fprintln(os.Stderr, PushRuleCheckTimeoutRemind)
			}
		}
		return 1, err
	}
	return int(returnCode), nil
}
